var $builtinmodule = function (name) {
    var mod = {};

    // todo; should probably put this in a math package
    mod.Mat44 = Sk.misceval.buildClass(mod, function ($gbl, $loc) {
            $loc.__init__ = new Sk.builtin.func(function (self) {
                Sk.misceval.callsimArray($loc.loadIdentity, [self]);
                self.stack = [];
            });

            $loc.push = new Sk.builtin.func(function (self) {
                self.stack.push(self.elements.slice(0));
            });

            $loc.pop = new Sk.builtin.func(function (self) {
                self.elements = self.stack.pop();
            });

            $loc.loadIdentity = new Sk.builtin.func(function (self) {
                self.elements = [1.0, 0.0, 0.0, 0.0,
                    0.0, 1.0, 0.0, 0.0,
                    0.0, 0.0, 1.0, 0.0,
                    0.0, 0.0, 0.0, 1.0];
            });

            $loc.transform3 = new Sk.builtin.func(function (self, v) {
                var e = self.elements;
                return Sk.misceval.callsimArray(mod.Vec3, [
                    e[0] * v.x + e[4] * v.y + e[8] * v.z,
                    e[1] * v.x + e[5] * v.y + e[9] * v.z,
                    e[2] * v.x + e[6] * v.y + e[10] * v.z]);
            });

            $loc.scale = new Sk.builtin.func(function (self, sx, sy, sz) {
                self.elements[0 * 4 + 0] *= sx;
                self.elements[0 * 4 + 1] *= sx;
                self.elements[0 * 4 + 2] *= sx;
                self.elements[0 * 4 + 3] *= sx;

                self.elements[1 * 4 + 0] *= sy;
                self.elements[1 * 4 + 1] *= sy;
                self.elements[1 * 4 + 2] *= sy;
                self.elements[1 * 4 + 3] *= sy;

                self.elements[2 * 4 + 0] *= sz;
                self.elements[2 * 4 + 1] *= sz;
                self.elements[2 * 4 + 2] *= sz;
                self.elements[2 * 4 + 3] *= sz;

                return self;
            });

            $loc.translate = new Sk.builtin.func(function (self, tx, ty, tz) {
                self.elements[3 * 4 + 0] += self.elements[0 * 4 + 0] * tx + self.elements[1 * 4 + 0] * ty + self.elements[2 * 4 + 0] * tz;
                self.elements[3 * 4 + 1] += self.elements[0 * 4 + 1] * tx + self.elements[1 * 4 + 1] * ty + self.elements[2 * 4 + 1] * tz;
                self.elements[3 * 4 + 2] += self.elements[0 * 4 + 2] * tx + self.elements[1 * 4 + 2] * ty + self.elements[2 * 4 + 2] * tz;
                self.elements[3 * 4 + 3] += self.elements[0 * 4 + 3] * tx + self.elements[1 * 4 + 3] * ty + self.elements[2 * 4 + 3] * tz;
                return self;
            });

            $loc.rotate = new Sk.builtin.func(function (self, angle, x, y, z) {
                var mag = Math.sqrt(x * x + y * y + z * z);
                var sinAngle = Math.sin(angle * Math.PI / 180.0);
                var cosAngle = Math.cos(angle * Math.PI / 180.0);

                if (mag > 0) {
                    var xx, yy, zz, xy, yz, zx, xs, ys, zs;
                    var oneMinusCos;
                    var rotMat;

                    x /= mag;
                    y /= mag;
                    z /= mag;

                    xx = x * x;
                    yy = y * y;
                    zz = z * z;
                    xy = x * y;
                    yz = y * z;
                    zx = z * x;
                    xs = x * sinAngle;
                    ys = y * sinAngle;
                    zs = z * sinAngle;
                    oneMinusCos = 1.0 - cosAngle;

                    rotMat = Sk.misceval.callsimArray(mod.Mat44);

                    rotMat.elements[0 * 4 + 0] = (oneMinusCos * xx) + cosAngle;
                    rotMat.elements[0 * 4 + 1] = (oneMinusCos * xy) - zs;
                    rotMat.elements[0 * 4 + 2] = (oneMinusCos * zx) + ys;
                    rotMat.elements[0 * 4 + 3] = 0.0;

                    rotMat.elements[1 * 4 + 0] = (oneMinusCos * xy) + zs;
                    rotMat.elements[1 * 4 + 1] = (oneMinusCos * yy) + cosAngle;
                    rotMat.elements[1 * 4 + 2] = (oneMinusCos * yz) - xs;
                    rotMat.elements[1 * 4 + 3] = 0.0;

                    rotMat.elements[2 * 4 + 0] = (oneMinusCos * zx) - ys;
                    rotMat.elements[2 * 4 + 1] = (oneMinusCos * yz) + xs;
                    rotMat.elements[2 * 4 + 2] = (oneMinusCos * zz) + cosAngle;
                    rotMat.elements[2 * 4 + 3] = 0.0;

                    rotMat.elements[3 * 4 + 0] = 0.0;
                    rotMat.elements[3 * 4 + 1] = 0.0;
                    rotMat.elements[3 * 4 + 2] = 0.0;
                    rotMat.elements[3 * 4 + 3] = 1.0;

                    rotMat = rotMat.multiply(self);
                    self.elements = rotMat.elements;
                }
                return self;
            });

            $loc.multiply = new Sk.builtin.func(function (self, right) {
                var tmp = Sk.misceval.callsimArray(mod.Mat44);

                for (var i = 0; i < 4; i++) {
                    tmp.elements[i * 4 + 0] =
                        (self.elements[i * 4 + 0] * right.elements[0 * 4 + 0]) +
                        (self.elements[i * 4 + 1] * right.elements[1 * 4 + 0]) +
                        (self.elements[i * 4 + 2] * right.elements[2 * 4 + 0]) +
                        (self.elements[i * 4 + 3] * right.elements[3 * 4 + 0]);

                    tmp.elements[i * 4 + 1] =
                        (self.elements[i * 4 + 0] * right.elements[0 * 4 + 1]) +
                        (self.elements[i * 4 + 1] * right.elements[1 * 4 + 1]) +
                        (self.elements[i * 4 + 2] * right.elements[2 * 4 + 1]) +
                        (self.elements[i * 4 + 3] * right.elements[3 * 4 + 1]);

                    tmp.elements[i * 4 + 2] =
                        (self.elements[i * 4 + 0] * right.elements[0 * 4 + 2]) +
                        (self.elements[i * 4 + 1] * right.elements[1 * 4 + 2]) +
                        (self.elements[i * 4 + 2] * right.elements[2 * 4 + 2]) +
                        (self.elements[i * 4 + 3] * right.elements[3 * 4 + 2]);

                    tmp.elements[i * 4 + 3] =
                        (self.elements[i * 4 + 0] * right.elements[0 * 4 + 3]) +
                        (self.elements[i * 4 + 1] * right.elements[1 * 4 + 3]) +
                        (self.elements[i * 4 + 2] * right.elements[2 * 4 + 3]) +
                        (self.elements[i * 4 + 3] * right.elements[3 * 4 + 3]);
                }

                self.elements = tmp.elements;
                return self;
            });

            /* Following gluLookAt implementation is adapted from
             * the Mesa 3D Graphics library. http://www.mesa3d.org
             */
            // todo; rewrite this with proper vec/mat ops
            $loc.lookAt = new Sk.builtin.func(function (self, eyeX, eyeY, eyeZ,
                centerX, centerY, centerZ,
                upX, upY, upZ) {
                /* Z vector */
                var z = [
                    eyeX - centerX,
                    eyeY - centerY,
                    eyeZ - centerZ
                ];
                var mag = Math.sqrt(z[0] * z[0] + z[1] * z[1] + z[2] * z[2]);
                if (mag) {
                    z[0] /= mag;
                    z[1] /= mag;
                    z[2] /= mag;
                }

                /* Y vector */
                var y = [upX, upY, upZ];

                /* X vector = Y cross Z */
                var x = [];
                x[0] = y[1] * z[2] - y[2] * z[1];
                x[1] = -y[0] * z[2] + y[2] * z[0];
                x[2] = y[0] * z[1] - y[1] * z[0];

                /* Recompute Y = Z cross X */
                y[0] = z[1] * x[2] - z[2] * x[1];
                y[1] = -z[0] * x[2] + z[2] * x[0];
                y[2] = z[0] * x[1] - z[1] * x[0];

                /* mpichler, 19950515 */
                /* cross product gives area of parallelogram, which is < 1.0 for
                * non-perpendicular unit-length vectors; so normalize x, y here
                */

                mag = Math.sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]);
                if (mag) {
                    x[0] /= mag;
                    x[1] /= mag;
                    x[2] /= mag;
                }

                mag = Math.sqrt(y[0] * y[0] + y[1] * y[1] + y[2] * y[2]);
                if (mag) {
                    y[0] /= mag;
                    y[1] /= mag;
                    y[2] /= mag;
                }

                var lookAt = Sk.misceval.callsimArray(mod.Mat44);
                lookAt.elements[0 * 4 + 0] = x[0];
                lookAt.elements[1 * 4 + 0] = x[1];
                lookAt.elements[2 * 4 + 0] = x[2];
                lookAt.elements[3 * 4 + 0] = 0.;
                lookAt.elements[0 * 4 + 1] = y[0];
                lookAt.elements[1 * 4 + 1] = y[1];
                lookAt.elements[2 * 4 + 1] = y[2];
                lookAt.elements[3 * 4 + 1] = 0.;
                lookAt.elements[0 * 4 + 2] = z[0];
                lookAt.elements[1 * 4 + 2] = z[1];
                lookAt.elements[2 * 4 + 2] = z[2];
                lookAt.elements[3 * 4 + 2] = 0.;
                lookAt.elements[0 * 4 + 3] = 0.;
                lookAt.elements[1 * 4 + 3] = 0.;
                lookAt.elements[2 * 4 + 3] = 0.;
                lookAt.elements[3 * 4 + 3] = 1.;

                // log(lookAt.elements);

                lookAt = lookAt.multiply(self);
                self.elements = lookAt.elements;
                self.translate(-eyeX, -eyeY, -eyeZ);

                // log(this.elements);

                return self;
            });
        },
        "Mat44", []);

    // todo; should probably put this in a math package
    mod.Mat33 = Sk.misceval.buildClass(mod, function ($gbl, $loc) {
            $loc.__init__ = new Sk.builtin.func(function (self) {
                Sk.misceval.callsimArray($loc.loadIdentity, [self]);
            });

            $loc.loadIdentity = new Sk.builtin.func(function (self) {
                self.elements = [1.0, 0.0, 0.0,
                    0.0, 1.0, 0.0,
                    0.0, 0.0, 1.0];
            });
        },
        "Mat33", []);

    mod.Vec3 = Sk.misceval.buildClass(mod, function ($gbl, $loc) {
            $loc.__init__ = new Sk.builtin.func(function (self, x, y, z) {
                self.x = x;
                self.y = y;
                self.z = z;
            });
            $loc.__sub__ = new Sk.builtin.func(function (self, other) {
                return Sk.misceval.callsimArray(mod.Vec3, [self.x - other.x, self.y - other.y, self.z - other.z]);
            });
        },
        "Vec3", []);

    mod.cross = new Sk.builtin.func(function (v1, v2) {
        Sk.asserts.assert(v1 instanceof mod.Vec3 && v2 instanceof mod.Vec3);
        return Sk.misceval.callsimArray(mod.Vec3, [
            v1.y * v2.z - v1.z * v2.y,
            v1.z * v2.x - v1.x * v2.z,
            v1.x * v2.y - v1.y * v2.x]);
    });

    return mod;
};
